package com.chentf.algorithm.algorithm.StringAndArrays;

import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

/**
 * 有效括号
 * 给定一个只包括 '('，')'，'{'，'}'，'['，']' 的字符串 s ，判断字符串是否有效。
 * 有效字符串需满足：
 * 左括号必须用相同类型的右括号闭合。
 * 左括号必须以正确的顺序闭合。
 * 每个右括号都有一个对应的相同类型的左括号
 * 示例 1：
 * 输入：s = "()"
 * 输出：true
 * 示例 2：
 * 输入：s = "()[]{}"
 * 输出：true
 * 示例 3：
 * 输入：s = "(]"
 * 输出：false
 */
public class ValidParentheses {

    public static void main(String[] args) {
        String s = "()[]{}";
        System.out.println(isValid(s));
    }

    /**
     * 判断括号的有效性可以使用「栈」这一数据结构来解决。
     *
     * 1.我们遍历给定的字符串 sss。当我们遇到一个左括号时，我们会期望在后续的遍历中，有一个相同类型的右括号将其闭合。由于后遇到的左括号要先闭合，因此我们可以将这个左括号放入栈顶。
     * 2.当我们遇到一个右括号时，我们需要将一个相同类型的左括号闭合。此时，我们可以取出栈顶的左括号并判断它们是否是相同类型的括号。如果不是相同的类型，或者栈中并没有左括号，那么字符串s无效，返回 False。为了快速判断括号的类型，我们可以使用哈希表存储每一种括号。哈希表的键为右括号，值为相同类型的左括号。
     * 3.在遍历结束后，如果栈中没有左括号，说明我们将字符串s中的所有左括号闭合，返回 True，否则返回 False。
     * 注意到有效字符串的长度一定为偶数，因此如果字符串的长度为奇数，我们可以直接返回 False\text{False}False，省去后续的遍历判断过程
     */

    public static boolean isValid(String s) {
        int n = s.length();
        // 有效字符串的长度一定为偶数,如果不是偶数,直接返回false
        if (n % 2 == 1) {
            return false;
        }
        // 存储每一种括号的对应关系
        Map<Character, Character> pairs = new HashMap<Character, Character>() {
            {
                put(')', '(');
                put(']', '[');
                put('}', '{');
            }
        };
        Deque<Character> stack = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            // 遇到左括号,将其放入栈顶
            char ch = s.charAt(i);
            // 遇到右括号,判断栈顶的左括号是否与当前右括号匹配
            if(pairs.containsKey(ch)){
                // 栈为空或者栈顶的左括号与当前右括号不匹配,返回false
                if(stack.isEmpty() || stack.peek()!= pairs.get(ch)){
                    return false;
                }
                // 栈顶的左括号与当前右括号匹配,将栈顶的左括号出栈
                stack.pop();
            }else{
                // 遇到左括号,将左括号入栈
                stack.push(ch);
            }
        }
        return stack.isEmpty();
    }
}
